/*---------------------------------------------------------------------------
//
//	Copyright(C) SMIT Corporation, 2000-2010.
//
//  File	:	sm_kapi.c
//	Purpose	:	Kernel API for all
//	History :
//				2004-09-10 created by pmy.
//
---------------------------------------------------------------------------*/

#include "sm_kapi.h"
#include "sm_trace.h"


/****************************************************************************
*
* Task functions
*
****************************************************************************/

sm_task_t	g_arTasks[SM_MAX_TASK_COUNT] = {0};

int SM_TaskEntry(void *arg)
{
	sm_task_t *task = NULL;
	
	task = (sm_task_t*)arg;
	SM_ObtainSem(&task->m_semtask, -1);
	task->m_task(task->m_taskdata);
	SM_DestroySem(&task->m_semtask);
	return 0;
}

unsigned int SM_CreateTask(
						   char                *name,          /* optional task name       */
						   int (*task_entry)(void *),         /* entry point function     */
						   void                *entry_data,    /* entry data               */
						   void                *stack_base,    /* stack base, NULL = alloc */
						   unsigned int        stack_size,     /* stack size, 0 = default  */
						   unsigned int		priority,       /* priority                 */
						   unsigned int		auto_start,		/* auto start task          */
						   unsigned int		taskAid)		
{
	int 			taskIndex;
	sm_task_t*		task = NULL;

	SM_TRACE_ENTRY(TRMOD_KAPI,"SM_CreateTask() - entry,proc=0x%08X name=%s statck_size=%d priority=%d auto_start=%d\r\n",
		(unsigned int)task, name, (int)stack_size, (int)priority, (int)auto_start);

	for (taskIndex = 0; taskIndex < SM_MAX_TASK_COUNT; taskIndex++)
	{
		if (g_arTasks[taskIndex].m_taskId == 0)
		{
			task = &g_arTasks[taskIndex];
			break;
		}
	}

	if (task == NULL)
	{
		SM_TRACE_ERROR(TRMOD_KAPI,"SM_CreateTask() - Too many task\r\n");
		return 0;
	}

	memset(task, 0x00, sizeof(sm_task_t));

	SM_CreateQueue(&task->m_queue, NULL);

	task->m_task = task_entry;
	task->m_taskdata = entry_data;
	SM_CreateSem(&task->m_semtask, "semtask", 0);
	
	//do not use CreateThread
	task->m_hThread = (unsigned int)SM_BeginThread (stack_size,
		(unsigned int (*) (void *))SM_TaskEntry,
		task,
		&task->m_taskId);
	memset(task->m_szName, 0x00, sizeof(task->m_szName));
	if(name)
		strcpy(task->m_szName, name);
	task->m_taskAid = taskAid;

	SM_TRACE_RETURN(TRMOD_KAPI,"SM_CreateTask() - return ok, name = %s,taskId=%d\r\n", 
		task->m_szName, task->m_taskId);
	SM_ReleaseSem(&task->m_semtask);
	return task->m_taskId;
}

unsigned long SM_FreeTaskId(unsigned long taskId)
{
	int 	taskIndex;

	for (taskIndex = 0; taskIndex < SM_MAX_TASK_COUNT; taskIndex++)
	{
		if (g_arTasks[taskIndex].m_taskId == taskId)
		{
			g_arTasks[taskIndex].m_taskId = 0;
			break;
		}
	}
	if (taskIndex >= SM_MAX_TASK_COUNT)
	{
		SM_TRACE_INFO(TRMOD_KAPI,"SM_FreeTaskId() - not find taskId=%ld\r\n", taskId);
		return -1;
	}

	return taskId;
}

void SM_KillTask(sm_task_t *task)
{
	SM_TRACE_ENTRY(TRMOD_KAPI,"SM_KillTask() - entry, task=0x%08X\r\n", task);

	SM_TRACE_INFO(TRMOD_KAPI,"SM_KillTask() - kill %s %d\r\n",
		task->m_szName, task->m_taskId);
	SM_DestroyThread(task->m_hThread);

	SM_TRACE_ENTRY(TRMOD_KAPI,"SM_KillTask() - return, task=0x%08X\r\n", task);
}

unsigned int SM_GetTaskIdByAid(unsigned char taskAid)
{
	int 	taskIndex;
	
	if (taskAid == 0)
	{
		SM_TRACE_ERROR(TRMOD_KAPI, "SM_GetTaskIdByAid() - taskAid invalid\r\n");
		return 0;
	}
		
	for (taskIndex = 0; taskIndex < SM_MAX_TASK_COUNT; taskIndex++)
	{
		if (g_arTasks[taskIndex].m_taskId
			&& g_arTasks[taskIndex].m_taskAid == taskAid)
		{
			return g_arTasks[taskIndex].m_taskId;
		}
	}
	
	return 0;
}


unsigned long SM_GetTaskAid(unsigned long taskId)
{
	unsigned long	taskAid = 0;
	int 			taskIndex;

	for (taskIndex = 0; taskIndex < SM_MAX_TASK_COUNT; taskIndex++)
	{
		if (g_arTasks[taskIndex].m_taskId == taskId)
		{
			taskAid = g_arTasks[taskIndex].m_taskAid;
			break;
		}
	}

	return taskAid;
}

int SM_SendQueueByTaskid(unsigned int taskId, const void* pMsg)
{
	int			taskIndex;
	sm_queue_t* queue = NULL;

    if(!pMsg)
    {
    	SM_TRACE_ERROR(TRMOD_KAPI, "SM_SendQueueByTaskid() - null msg\r\n");
    	return 0;
    }

	if (taskId == 0)
	{
		SM_TRACE_ERROR(TRMOD_KAPI, "SM_SendQueueByTaskid() - taskId=%d invalid\r\n", 0);
		return 0;
	}
		
	for (taskIndex = 0; taskIndex < SM_MAX_TASK_COUNT; taskIndex++)
	{
		if (g_arTasks[taskIndex].m_taskId == taskId)
		{
			queue = &g_arTasks[taskIndex].m_queue;
			break;
		}
	}

	if (queue == NULL)
	{
		SM_TRACE_ERROR(TRMOD_KAPI, "SM_SendQueueByTaskid() - taskId=%d not found\r\n", taskId);
		return 0;
	}

	return SM_SendQueue(queue, pMsg);			
}


int SM_ReceiveQueueByTaskid(unsigned int taskId, void* pMsg, unsigned long timeout_ms)
{
	int			taskIndex;
	sm_queue_t* queue = NULL;
	int		rc;

	SM_TRACE_ENTRY(TRMOD_KAPI, "SM_ReceiveQueueByTaskid() - entry,"
				"taskId=%lu,pMsg=0x%08X,timeout_ms=%dms\r\n",
				taskId, pMsg, timeout_ms);
	
    if(!pMsg)
    {
    	SM_TRACE_ERROR(TRMOD_KAPI, "SM_ReceiveQueueByTaskid() - null msg\r\n");
    	return 0;
    }

	if (taskId == 0)
	{
		SM_TRACE_ERROR(TRMOD_KAPI, "SM_ReceiveQueueByTaskid() - taskId=%d invalid\r\n", 0);
		return 0;
	}
		
	for (taskIndex = 0; taskIndex < SM_MAX_TASK_COUNT; taskIndex++)
	{
		if (g_arTasks[taskIndex].m_taskId == taskId)
		{
			queue = &g_arTasks[taskIndex].m_queue;
			break;
		}
	}

	if (queue == NULL)
	{
		SM_TRACE_ERROR(TRMOD_KAPI, "SM_ReceiveQueueByTaskid() - taskId=%d not found\r\n", taskId);
		return 0;
	}
	
	rc = SM_ReceiveQueue(queue, pMsg, timeout_ms);		

	SM_TRACE_RETURN(TRMOD_KAPI, "SM_ReceiveQueueByTaskid() - entry,taskId=%lu,rc=%d\r\n",
				taskId, rc);
	return rc;
}


/****************************************************************************
*
* Queue functions
*
****************************************************************************/

void SM_CreateQueue(sm_queue_t* queue, char* name)
{	
	SM_TRACE_ENTRY(TRMOD_KAPI, "SM_CreateQueue() - entry,queue=0x%08X,name=%s\r\n",
					queue, name);
	
	SM_CreateSem(&queue->m_semLock,"semQlock", 1);
	SM_CreateSem(&queue->m_semSend,"semQsend", 0);
	SM_CreateSem(&queue->m_semRecv,"semQrecv", 0);
	
	memset(queue->m_items, 0x00, sizeof(queue->m_items));
	queue->m_head = 0;
	queue->m_tail = 0;
	
	SM_TRACE_RETURN(TRMOD_KAPI, "SM_CreateQueue() - return,queue=0x%08X\r\n",queue);
}


void SM_DestroyQueue(sm_queue_t *queue)
{
	SM_TRACE_ENTRY(TRMOD_KAPI, "SM_DestroyQueue() - entry,queue=0x%08X\r\n",queue);

	SM_DestroySem(&queue->m_semLock);
	SM_DestroySem(&queue->m_semSend);
	SM_DestroySem(&queue->m_semRecv);
	memset(queue->m_items, 0x00, sizeof(queue->m_items));
	queue->m_head = 0;
	queue->m_tail = 0;
	
	SM_TRACE_RETURN(TRMOD_KAPI, "SM_DestroyQueue() - return,queue=0x%08X\r\n",queue);
}


int SM_SendQueue(sm_queue_t* queue, const void* pMsg)
{
	SM_TRACE_ENTRY(TRMOD_KAPI, "SM_SendQueue() - entry,queue=0x%08X,pMsg=0x%08X\r\n",queue,pMsg);
	
    if(!pMsg)
    {
    	SM_TRACE_ERROR(TRMOD_KAPI, "SM_SendQueue() - null msg\r\n");
    	return 0;
    }

	SM_ObtainSem(&queue->m_semLock, -1);

	if ((queue->m_tail + 1) % SM_QUEUE_MAX == queue->m_head) //queue full
	{
		SM_ReleaseSem(&queue->m_semLock);
		SM_ObtainSem(&queue->m_semRecv, -1);
		SM_ObtainSem(&queue->m_semLock, -1);
    }
 
    memcpy(&queue->m_items[queue->m_tail], pMsg, SM_QUEUE_LEN) ;
	queue->m_tail = (queue->m_tail + 1) % SM_QUEUE_MAX;

	SM_ReleaseSem(&queue->m_semSend);	
		
	SM_ReleaseSem(&queue->m_semLock);
	
	SM_TRACE_RETURN(TRMOD_KAPI, "SM_SendQueue() - return true,queue=0x%08X\r\n",queue);
    return 1;	
}


int SM_ReceiveQueue(sm_queue_t* queue, void* pMsg, unsigned long timeout_ms)
{
	int		isPrevFull = 0;

	SM_TRACE_ENTRY(TRMOD_KAPI, "SM_ReceiveQueue() - entry,"
				"queue=0x%08X,pMsg=0x%08X,timeout_ms=%dms\r\n",
				queue, pMsg, timeout_ms);
	
    if(!pMsg)
    {
    	SM_TRACE_ERROR(TRMOD_KAPI, "SM_ReceiveQueue() - null msg\r\n");
    	return 0;
    }
        
   	if (!SM_ObtainSem(&queue->m_semSend, timeout_ms))
   	{
   		SM_TRACE_NOTIFY(TRMOD_KAPI, "SM_ReceiveQueue() - wait queue timeout=%dms\r\n", timeout_ms);
   		return 0;
   	}

	SM_ObtainSem(&queue->m_semLock, -1);	
	
	if (queue->m_head == queue->m_tail)
	{
		SM_TRACE_INFO(TRMOD_KAPI, "SM_ReceiveQueue() - queue empty\r\n");
		SM_ReleaseSem(&queue->m_semLock);
		return 0;
	}
	
	if ((queue->m_tail + 1) % SM_QUEUE_MAX == queue->m_head)
		isPrevFull = 1;		
		
    memcpy(pMsg, &queue->m_items[queue->m_head], SM_QUEUE_LEN) ;
	queue->m_head = (queue->m_head + 1) % SM_QUEUE_MAX;

	if (isPrevFull)
		SM_ReleaseSem(&queue->m_semRecv);		
		
	SM_ReleaseSem(&queue->m_semLock);	
	
	SM_TRACE_RETURN(TRMOD_KAPI, "SM_ReceiveQueue() - return true,"
				"queue=0x%08X\r\n",	queue);
    return 1;
}

#ifndef countof
#define countof(array) (sizeof(array)/sizeof(array[0]))
#endif

unsigned int SM_BeginThread (unsigned int _StackSize,
							 unsigned int (*_StartAddress) (void *), 									
							 void * _ArgList,
							 unsigned int* _ThrdAddr)

{
	return SM_OsThreadCreate(_StackSize, _StartAddress, _ArgList, _ThrdAddr);
}

void SM_DestroyThread(unsigned int hThread)
{
	SM_OsThreadDestroy(hThread);
}

unsigned int SM_GetCurTaskId(void)
{
	return SM_OsThreadGetCurId();
}

/****************************************************************************
*
* Semaphore functions
*
****************************************************************************/

void SM_CreateSem(
				  sm_sem_t    *semaphore,         /* Semaphore to init       */
				  char				 *name,
				  unsigned int		 initial_count)      /* Initial semaphore value */
{
	SM_OsSemCreate(semaphore, name, initial_count);
}


void SM_DestroySem(sm_sem_t *semaphore )
{
	SM_OsSemDestroy(semaphore);
}


int SM_ObtainSem(sm_sem_t *semaphore, unsigned long timeout_ms)
{
	return SM_OsSemWait(semaphore, timeout_ms);
}


void SM_ReleaseSem(sm_sem_t *semaphore)
{
	SM_OsSemRelease(semaphore);
}


/****************************************************************************
*
* Time functions
*
****************************************************************************/


unsigned long SM_GetMsecCount(void)
{
	return SM_OsMsecCountGet();
}

unsigned long SM_GetTickCount(void)
{
	return SM_OsTickCountGet();
}


void SM_Sleep(unsigned long sleep_ms)
{
	SM_OsThreadSleep(sleep_ms);
}

/****************************************************************************
*
* Memory functions
*
****************************************************************************/

#ifdef DEBUG
static long 		s_nMemCount = 0;
static sm_sem_t 	s_semMem;
struct tagMEMELEMENT
{
	unsigned long		m_size;
	void*				m_memblock;
	unsigned long		m_taskId;
	unsigned long		m_tick;
	SAPI_BOOL			m_isrealloc;
}s_MemStateTable[1000];


void* SM_Malloc(unsigned long size)
{
	static SAPI_BOOL s_isSemInited = SAPI_FALSE;	

	void*	memblock = NULL;

	if (!s_isSemInited)
	{
		SM_CreateSem(&s_semMem, "mallocsem", 1);
		SM_Printf("s_MemStateTable size=%d count=%d\r\n",
			countof(s_MemStateTable), sizeof(s_MemStateTable));
		s_isSemInited = SAPI_TRUE;
	}

	if (size <= 0)
	{
		SM_Printf("SM_Malloc() - size=%ld is invalid\r\n", size);
		return NULL;
	}

	SM_ObtainSem(&s_semMem, -1);

	if (s_nMemCount < countof(s_MemStateTable))
	{
		memblock = INT_MemMalloc(size);
		if (memblock)
		{
			s_MemStateTable[s_nMemCount].m_tick = SM_GetTickCount();
			s_MemStateTable[s_nMemCount].m_memblock = memblock;
			s_MemStateTable[s_nMemCount].m_size = size;
			s_MemStateTable[s_nMemCount].m_taskId = SM_GetCurTaskId();
			s_MemStateTable[s_nMemCount].m_isrealloc = SAPI_FALSE;
			if (size >= 20)
			{
				SM_Printf("SM_Malloc() - %d : taskId=%d tick=%d "
					"addr=0x%08X size=%d isrealloc=%d\r\n",
					s_nMemCount,
					(int)s_MemStateTable[s_nMemCount].m_taskId,
					(int)s_MemStateTable[s_nMemCount].m_tick,
					(int)s_MemStateTable[s_nMemCount].m_memblock,
					(int)s_MemStateTable[s_nMemCount].m_size,
					(int)s_MemStateTable[s_nMemCount].m_isrealloc);
			}
			s_nMemCount++;
		}
	}
	else
	{
		SM_Printf("SM_Malloc() - memory out, current count=%d\r\n", s_nMemCount);
	}

	SM_ReleaseSem(&s_semMem);

	return memblock;
}


void SM_Free(void* memblock)
{
	long	i;

	SM_ObtainSem(&s_semMem, -1);

	if (memblock)
	{
		for (i = 0; i < s_nMemCount; i++)
		{
			if (memblock == s_MemStateTable[i].m_memblock)
				break;
		}
		if (i  < s_nMemCount)
		{
			for (; i < s_nMemCount - 1; i++)
				s_MemStateTable[i] = s_MemStateTable[i+1];
			s_nMemCount--;
		}
		else
		{
			SM_TRACE_ERROR(TRMOD_KAPI, "SM_Free found memblock=%p not allocated\r\n", memblock);
			SM_ReleaseSem(&s_semMem);
			return;
		}
		INT_MemFree(memblock);
	}

	SM_ReleaseSem(&s_semMem);
}


void* SM_Realloc(void *memblock, unsigned long size)
{
	long	i;
	long	findIndex = -1;

	if (memblock && 0 == size)
	{
		SM_Free(memblock);
		memblock = NULL;
	}
	else
	{
		SM_ObtainSem(&s_semMem, -1);
		for (i = 0; i < s_nMemCount; i++)
		{
			if (memblock == s_MemStateTable[i].m_memblock)
			{
				findIndex = i;
				break;
			}
		}

		//realloc for NULL
		if(!memblock && s_nMemCount<countof(s_MemStateTable))
		{
			findIndex = s_nMemCount;
			s_nMemCount++;
		}

		memblock = INT_MemRealloc(memblock, size);
		if (memblock)
		{
			s_MemStateTable[findIndex].m_tick = SM_GetTickCount();
			s_MemStateTable[findIndex].m_memblock = memblock;
			s_MemStateTable[findIndex].m_size = size;
			s_MemStateTable[findIndex].m_taskId = SM_GetCurTaskId();
			s_MemStateTable[findIndex].m_isrealloc = SAPI_TRUE;
		}
		SM_ReleaseSem(&s_semMem);
	}

	return memblock;
}


void SM_Memleak(void)
{

	long i;

	SM_ObtainSem(&s_semMem, -1);
	SM_Printf("SM_Memleak() - memory block count = %d\r\n", s_nMemCount);
	for (i = 0; i < s_nMemCount; i++)
	{
		SM_Printf("%d : taskId=%d tick=%d "
			"addr=0x%08X size=%d isrealloc=%d\r\n",
			i,
			(int)s_MemStateTable[i].m_taskId,
			(int)s_MemStateTable[i].m_tick,
			(int)s_MemStateTable[i].m_memblock,
			(int)s_MemStateTable[i].m_size,
			(int)s_MemStateTable[i].m_isrealloc);
	}
	SM_ReleaseSem(&s_semMem);
	SM_DestroySem(&s_semMem);
}
#else

void* SM_Malloc(unsigned long size)
{
	return SM_OsMemAllocate(size);
}

void SM_Free(void* memblock)
{
	SM_OsMemFree(memblock);
}

void* SM_Realloc(void *memblock, unsigned long size)
{
	return SM_OsMemRealloc(memblock, size);
}


#endif
